package net.shambolica.ember.jinterface.wrappers.objects;

import com.ericsson.otp.erlang.OtpErlangObject;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Patterns can be:
 * <br/>- Matched.
 * <br/>- Instantiated (non-predicates, at least).
 */
public abstract class EOPattern {

    /**
     * Attempt to pattern-match on a term. This is essentially match(term, matchKey, null).
     * @param term      The term to match against.
     * @return          Var match map, expected to be immutable.
     */
    public final Map<String,OtpErlangObject> match(OtpErlangObject term) {
        return match(term, null);
    }


    /**
     * Attempt to (recursively) pattern-match on a term.
     * @param term      The term to match against.
     * @param acc       Accumulator var match map. Expected to be immutable.
     *                  If null, an empty map is implicitly created.
     * @return          Var match map, expected to be immutable.
     */
    public abstract Map<String,OtpErlangObject> match(OtpErlangObject term,
                                                      Map<String,OtpErlangObject>  acc);


    /**
     * Create an instance of this pattern, if not unbound/abstract.
     * @return          An OtpErlangObject corresponding to the pattern, or null
     *                  if unbound/abstract.
     */
    public OtpErlangObject instantiate() {
        if (isAbstract()) {
            return null;
        } else {
            throw new Error("not expected to instantiate a "+this.getClass().getName());
        }
    }


    /**
     * Returns true if cannot be instantiated (i.e. contains predicates or variable bindings).
     * @return true if cannot be instantiated.
     */
    public abstract boolean isAbstract();


    /**
     * Utility method: if acc is null, return an empty, immutable map. Otherwise, return acc.
     * @param acc   A supposed accumulator for matching.
     * @return      A non-null accumulator object.
     */
    public static Map<String,OtpErlangObject> mkAcc(Map<String,OtpErlangObject> acc) {
        return acc != null ? acc : new Map<String, OtpErlangObject>() {
            public int size() {
                return 0;
            }

            public boolean isEmpty() {
                return true;
            }

            public boolean containsKey(Object o) {
                return false;
            }

            public boolean containsValue(Object o) {
                return false;
            }

            public OtpErlangObject get(Object o) {
                return null;
            }

            public OtpErlangObject put(String s, OtpErlangObject otpErlangObject) {
                return null;
            }

            public OtpErlangObject remove(Object o) {
                return null;
            }

            public void putAll(Map<? extends String, ? extends OtpErlangObject> map) {
            }

            public void clear() {
            }

            public Set<String> keySet() {
                return new HashSet<String>(0);
            }

            public Collection<OtpErlangObject> values() {
                return new HashSet<OtpErlangObject>(0);
            }

            public Set<Entry<String, OtpErlangObject>> entrySet() {
                return new HashSet<Entry<String, OtpErlangObject>>(0);
            }
        };
    }

}
